package org.dromara.common.redis.manager

import org.dromara.common.core.utils.SpringUtils.getBean
import org.springframework.cache.Cache
import java.util.concurrent.Callable

/**
 * Cache 装饰器模式(用于扩展 Caffeine 一级缓存)
 *
 * @author LikeYouDo
 * @date 2025/7/16
 */

open class CaffeineCacheDecorator(
    private val name: String,
    private val cache: Cache
) : Cache {

    companion object {
        private val CAFFEINE: com.github.benmanes.caffeine.cache.Cache<Any, Any?> = getBean("caffeine")
    }

    override fun getName(): String {
        return name
    }

    override fun getNativeCache(): Any {
        return cache.nativeCache
    }

    fun getUniqueKey(key: Any): String {
        return "$name:$key"
    }

    override fun get(key: Any): Cache.ValueWrapper? {
        val o: Any? = CAFFEINE.get(getUniqueKey(key)) { k: Any -> cache.get(key) }
        return o as Cache.ValueWrapper?
    }

    @Suppress("UNCHECKED_CAST")
    override fun <T> get(key: Any, type: Class<T>?): T? {
        val o = CAFFEINE.get(getUniqueKey(key)) { k: Any -> cache.get(key, type) }
        return o as T?
    }

    override fun put(key: Any, value: Any?) {
        CAFFEINE.invalidate(getUniqueKey(key))
        cache.put(key, value)
    }

    override fun putIfAbsent(key: Any, value: Any?): Cache.ValueWrapper? {
        CAFFEINE.invalidate(getUniqueKey(key))
        return cache.putIfAbsent(key, value)
    }

    override fun evict(key: Any) {
        evictIfPresent(key)
    }

    override fun evictIfPresent(key: Any): Boolean {
        val b = cache.evictIfPresent(key)
        if (b) {
            CAFFEINE.invalidate(getUniqueKey(key))
        }
        return b
    }

    override fun clear() {
        CAFFEINE.invalidateAll()
        cache.clear()
    }

    override fun invalidate(): Boolean {
        return cache.invalidate()
    }

    @Suppress("UNCHECKED_CAST")
    override fun <T> get(key: Any, valueLoader: Callable<T>): T? {
        val o: Any? = CAFFEINE.get(getUniqueKey(key)) { k: Any? -> cache.get(key, valueLoader) }
        return o as T?
    }
}
